//
//	MiscTee.h -- An object which will pass a message on to any of the objects
//				 connected to it.  This is useful for splitting an action
//				 message to multiple recipients or for allowing multiple
//				 delegates to be connected to provide different services.
//
//		Written by David Fedchenko.  Copyright 1994 by David Fedchenko.
//				Version 1.1  All rights reserved.
//		Additions by Don Yacktman to remove warnings and avoid infinite loops.
//
//		This notice may not be removed from this source code.
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	

#import "MiscTee.h"

typedef struct
	{
	// Object *idObject; // Using this would remove warnings _if_ isa were
	// not protected, but as things stand, we'll have to live with the
	// warnings.  :-(  That's what you get for "isa hacking"!
	//id idObject; // This is where we got warnings...original definition...
	struct objc_class *idObject; // This seems to work right...
	SEL	selAction;
	} CONPAIR;

@implementation MiscTee

- init
	{
	[super init];
	inTee = NO;	
	idConnections = [[Storage alloc] initCount:0 elementSize:sizeof(CONPAIR)
									 description:@encode(CONPAIR)];
	return self;
	}

- free
	{
	[idConnections free];
	
	return [super free];
	}

- addConnection:anObject with:(SEL)anAction
	{
	CONPAIR cp;
	CONPAIR * pcp;
	int i;
	
	cp.idObject = anObject;
	cp.selAction = anAction;
	
	// replace the action if the object is already in the list
	for (i = 0; i < [idConnections count]; i++)
		{
		pcp = (CONPAIR *)[idConnections elementAt:i];
		if (pcp->idObject == anObject)
			{
			pcp->selAction = anAction;
			return self;
			}
		}
	
	// new connection
	[idConnections addElement:&cp];
	
	return self;
	}

- removeConnection:anObject
	{
	CONPAIR * pcp;
	int i;
	
	for (i = 0; i < [idConnections count]; i++)
		{
		pcp = (CONPAIR *)[idConnections elementAt:i];
		if (pcp->idObject == anObject)
			{
			[idConnections removeElementAt:i];
			break;
			}
		}
	
	return self;
	}

- ping:sender
{
	int			i;
	CONPAIR *	pcp;

	if (inTee) return self;
	inTee = YES;
	for (i = 0; i < [idConnections count]; i++) {
		pcp = (CONPAIR *)[idConnections elementAt:i];
		if (strcmp(pcp->idObject->isa->name, "FREED(id)") &&
				pcp->idObject != sender && pcp->selAction) {
			[pcp->idObject perform:pcp->selAction with:sender];
		}
	}
	inTee = NO;
	return self;
}

- forward:(SEL)aSelector :(marg_list)argFrame
	{
	int			i;
	CONPAIR *	pcp;
	void *		pv = nil;
	BOOL		fSent = NO;
	
	if (inTee) return self;
	inTee = YES;
	for (i = 0; i < [idConnections count]; i++)
		{
		pcp = (CONPAIR *)[idConnections elementAt:i];
		if (strcmp(pcp->idObject->isa->name, "FREED(id)") &&
			[pcp->idObject respondsTo:aSelector])
			{
			pv = [pcp->idObject performv:aSelector :argFrame];
			fSent = YES;
			}
		}
	
	if (!fSent)
		{
		[self doesNotRecognize:aSelector];
		}
	inTee = NO;

	return (id)pv;
	}

-(BOOL) respondsTo:(SEL)aSelector
	{
	int			i;
	CONPAIR *	pcp;
	
	if ([super respondsTo:aSelector])
		{
		return YES;
		}
	
	for (i = 0; i < [idConnections count]; i++)
		{
		pcp = (CONPAIR *)[idConnections elementAt:i];
		if (strcmp(pcp->idObject->isa->name, "FREED(id)") &&
			[pcp->idObject respondsTo:aSelector])
			{
			// don't worry about multiple positive responses
			return YES;
			}
		}
	
	return NO;
	}

- read:(NXTypedStream *)stream
	{
	[super read:stream];
	
	idConnections = NXReadObject(stream);
	inTee = NO;
	return self;
	}

- write:(NXTypedStream *)stream
	{
	[super write:stream];
	
	NXWriteObject(stream, idConnections);
	
	return self;
	}

@end
